package com.esgov.chatbot.exceptionHandlers;

import com.esgov.chatbot.support.ResponseResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import java.util.stream.Collectors;

/**
 * 全局Controller异常处理器
 *
 * @author Eric Lao
 * @since 2023/8/8 11:26
 */
@Slf4j
@ControllerAdvice
@Order()
public class GlobalExceptionHandler {

    /**
     * BindException对Get请求中以表单提交的参数校验有效。
     * MethodArgumentNotValidException用于捕获@RequestBody类型参数触发校验规则抛出的异常
     */
    @ExceptionHandler(value = {BindException.class, MethodArgumentNotValidException.class})
    public ResponseEntity<ResponseResult<Void>> bindExceptionHandler(HttpServletRequest request, BindException e) {
        String errorMessage = "参数校验未通过：" + e.getBindingResult().getAllErrors().stream()
                .map(DefaultMessageSourceResolvable::getDefaultMessage)
                .filter(StringUtils::hasLength)
                .collect(Collectors.joining(";"));
        log.warn("==> 请求路径{}, {}", request.getRequestURI(), errorMessage);
        return new ResponseEntity<>(ResponseResult.fail4("参数异常"), HttpStatus.BAD_REQUEST);
    }

    /**
     * 在controller添加@Validated，对@RequestParam和@PathVariable注解的参数校验有效
     */
    @ExceptionHandler(value = {ConstraintViolationException.class})
    public ResponseEntity<ResponseResult<Void>> constraintViolationExceptionHandler(HttpServletRequest request, ConstraintViolationException e) {
        String errorMessage = "参数校验错误：" + e.getMessage();
        log.warn("==> 请求路径{}, {}", request.getRequestURI(), errorMessage);
        return new ResponseEntity<>(ResponseResult.fail4("参数异常"), HttpStatus.BAD_REQUEST);
    }

    /**
     * 捕获参数缺失异常。Required request parameter xxx for method parameter type xx is not present
     */
    @ExceptionHandler(value = {MissingServletRequestParameterException.class})
    public ResponseEntity<ResponseResult<Void>> missingServletRequestParameterExceptionHandler(HttpServletRequest request, MissingServletRequestParameterException e) {
        String errorMessage = "参数缺失：" + e.getMessage();
        log.warn("==> 请求路径{}, {}", request.getRequestURI(), errorMessage);
        return new ResponseEntity<>(ResponseResult.fail4("参数异常"), HttpStatus.BAD_REQUEST);
    }

    /**
     * 用于捕获@RequestBody参数Bean里面的变量类型跟请求的参数变量类型不一致的异常。
     * 如实体DemoRequest中有个变量size为Integer类型，但是前端传递的参数size为String
     */
    @ExceptionHandler(value = {HttpMessageNotReadableException.class})
    public ResponseEntity<ResponseResult<Void>> httpMessageNotReadableExceptionHandler(HttpServletRequest request, HttpMessageNotReadableException e) {
        String errorMessage = "参数body部分解析异常：" + e.getMessage();
        log.warn("==> 请求路径{}, {}", request.getRequestURI(), errorMessage);
        return new ResponseEntity<>(ResponseResult.fail4("参数异常"), HttpStatus.BAD_REQUEST);
    }

    /**
     * 全局捕获接口异常，统一返回
     */
    @ExceptionHandler(value = {Exception.class})
    public ResponseEntity<ResponseResult<Void>> exceptionHandler(HttpServletRequest request, Exception e) {
        String errorMessage = "接口异常：" + e.getMessage();
        log.error("==> 请求路径{}, {}", request.getRequestURI(), errorMessage, e);
        return new ResponseEntity<>(ResponseResult.fail4("参数异常"), HttpStatus.INTERNAL_SERVER_ERROR);
    }

}
